﻿using System;
using System.Drawing;
using System.IO;

namespace LeRenard
{
    /// <summary>
    /// Provides extension methods to interact with <see cref="Image"/> objects.
    /// </summary>
	public static class ImageExtensions
    {
        /// <summary>
        /// Proportionally resizes an image.
        /// </summary>
        /// <param name="image">The image to resize</param>
        /// <param name="maxWidth">Maximum allowed width.</param>
        /// <param name="maxHeight">Maximul allowed height.</param>
        /// <returns>A resized <see cref="Image"/>.</returns>
        public static Image Resize(this Image image, int maxWidth, int maxHeight)
        {
            // Get the original image
            var originalImage = image;

            // Calculate the ratios
            var ratioX = (double)maxWidth / originalImage.Width;
            var ratioY = (double)maxHeight / originalImage.Height;
            var ratio = Math.Min(ratioX, ratioY);

            // Calculate new width
            var newWidth = (int)(originalImage.Width * ratio);
            var newHeight = (int)(originalImage.Height * ratio);

            // Generate the new image
            var newImage = new Bitmap(newWidth, newHeight);
            using (var graphics = Graphics.FromImage(newImage)) {
                graphics.DrawImage(originalImage, 0, 0, newWidth, newHeight);
            }

            // Return proportionally resized image
            return newImage;
        }

        /// <summary>
        /// Proportionally resizes the provided image, but returns a rectangle with provided maxWidth and maxHeight.
        /// </summary>
        /// <param name="image">The <see cref="Image"/> to resize.</param>
        /// <param name="maxWidth">Maximum allowed width.</param>
        /// <param name="maxHeight">Maximul allowed height.</param>
        /// <param name="backgroundColor">Background color used to fill the remaining upper and side space.</param>
        /// <returns>A resized <see cref="Image"/> at maxWidth X maxHeight.</returns>
        public static Image ResizeWithPadding(this Image image, int maxWidth, int maxHeight, Color? backgroundColor = null)
        {
            ThrowIf.Null(() => image);

            if (backgroundColor == null)
                backgroundColor = Color.White;

            // Resize the image proportionally
            var resized = image.Resize(maxWidth, maxHeight);
            var newImage = new Bitmap(maxWidth, maxHeight);

            // Calculate point to start drawing image
            var left = (maxWidth - resized.Width) / 2;
            var top = (maxHeight - resized.Height) / 2;

            // Center it and paint the background in desired color
            using (var graphics = Graphics.FromImage(newImage)) {
                graphics.FillRectangle(new SolidBrush(backgroundColor.Value), new Rectangle(0, 0, maxWidth, maxHeight));
                graphics.DrawImage(resized, left, top);
            }

            // Return the image
            return newImage;
        }

        /// <summary>
        /// Proportionally resizes the provided image, but returns a rectangle with provided maxWidth and maxHeight.
        /// </summary>
        /// <param name="image">The <see cref="Image"/> to resize.</param>
        /// <param name="maxWidth">Maximum allowed width.</param>
        /// <param name="maxHeight">Maximul allowed height.</param>
        /// <param name="backgroundImage">Background image used to fill the remaining upper and side space.</param>
        /// <returns>A resized <see cref="Image"/> at maxWidth X maxHeight.</returns>
        public static Image ResizeWithPadding(this Image image, int maxWidth, int maxHeight, Image backgroundImage = null)
        {
            ThrowIf.Null(() => image);

            if (backgroundImage == null) {
                return image.ResizeWithPadding(maxWidth, maxHeight, backgroundColor: null);
            }

            // Resize the image proportionally
            var resized = image.Resize(maxWidth, maxHeight);
            var newImage = new Bitmap(maxWidth, maxHeight);

            // Calculate point to start drawing image
            var left = (maxWidth - resized.Width) / 2;
            var top = (maxHeight - resized.Height) / 2;

            // Center it and paint the background in desired color
            using (var graphics = Graphics.FromImage(newImage)) {
                graphics.DrawImage(backgroundImage, 0, 0, maxWidth, maxHeight);
                graphics.DrawImage(resized, left, top);
            }

            // Return the image
            return newImage;
        }

        /// <summary>
        /// Converts an <see cref="Image"/> to a <see cref="Byte"/> array.
        /// </summary>
        /// <param name="image"></param>
        /// <returns>An array of <see cref="Byte"/> containing the image data.</returns>
        public static byte[] ToByteArray(this Image image)
        {
            ThrowIf.Null(() => image);

            using (var memoryStream = new MemoryStream()) {
                image.Save(memoryStream, image.RawFormat);
                return memoryStream.ToArray();
            }
        }
    }
}
